package com.github.cyf.async.service;

import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;
import com.github.cyf.async.context.OauthContext;
import com.github.cyf.async.entity.LoginVal;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 异步线程数据传递
 * @author chenyifan
 * @create 2025-05-08 11:01
 */
@Service
@Slf4j
public class AsyncDataTransferService {

    /**
     * 模拟登录
     */
    private void login() {
        OauthContext.set(new LoginVal("cyf", "123"));
    }

    @Autowired
    private ThreadPoolTaskExecutor threadPool;

    /**
     * 手动设置异步线程数据
     */
    public void manualSet() {
        login();
        LoginVal loginVal = OauthContext.get();
        log.info("主线程数据：{}", loginVal);
        threadPool.execute(() -> {
            OauthContext.set(loginVal);
            log.info("异步线程数据：{}", OauthContext.get());
        });
    }

    /**
     * 线程池设置 TaskDecorator
     */
    public void threadPoolSet() {
        login();
        LoginVal loginVal = OauthContext.get();
        log.info("主线程数据：{}", loginVal);
        threadPool.execute(() -> {
            log.info("异步线程数据：{}", OauthContext.get());
        });
    }

    /**
     * InheritableThreadLocal 传递线程数据，子线程可以继承父线程的数据，
     * InheritableThreadLocal 原理：在创建线程时，将父线程的 InheritableThreadLocal 中的数据复制到子线程中，但是已创建的线程是无法继承的，
     * 可以使用阿里巴巴的 TTL 来解决这个问题
     */
    public void inheritableSet() {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        ThreadLocal<String> username = new InheritableThreadLocal<>();
        for (int i = 0; i < 10; i++) {
            username.set("tcxld--" + i);
            threadPool.execute(() -> log.info(username.get()));
        }
    }

    /**
     * 阿里巴巴的 TTL 解决 InheritableThreadLocal 传递线程数据的问题
     */
    public void ttlSet() {
        Executor threadPool = Executors.newFixedThreadPool(3);
        threadPool = TtlExecutors.getTtlExecutor(threadPool);

        ThreadLocal<String> username = new TransmittableThreadLocal<>();
        for (int i = 0; i < 10; i++) {
            username.set("tcxld--" + i);
            threadPool.execute(() -> log.info(username.get()));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}